package org.pixelami.hercularity.tests
{
	
	import flash.events.ErrorEvent;
	import flash.events.Event;
	
	import flexunit.framework.Assert;
	
	import org.flexunit.async.Async;
	import org.pixelami.hercularity.Step;
	import org.pixelami.hercularity.conditions.Condition;
	import org.pixelami.hercularity.descriptors.Action;
	import org.pixelami.hercularity.descriptors.AsyncAction;
	import org.pixelami.hercularity.events.StepErrorEvent;
	import org.pixelami.hercularity.events.StepEvent;
	import org.pixelami.hercularity.tests.mocks.StepConditions;
	import org.pixelami.hercularity.tests.mocks.TestTaskAsync;
	
	
	public class StepTests
	{		
		[Before]
		public function setUp():void
		{
		}
		
		[After]
		public function tearDown():void
		{
		}
		
		[BeforeClass]
		public static function setUpBeforeClass():void
		{
		}
		
		[AfterClass]
		public static function tearDownAfterClass():void
		{
		}
		
		static public var FAIL:Boolean = false;
		static public var SUCCEED:Boolean = true;
		
		
		
		[Test(async,timeout="1000")]
		public function testStepWithMixedActions():void
		{
			var s:Step = new Step(
				new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR),
				new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR)
			).thenDo(
				new Step(
					new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR),
					new AsyncAction(new TestTaskAsync(TestTaskAsync.FAIL),Action.PROCEED_ON_ERROR)
				).thenIf(
					new Condition(StepConditions,"AtLeastOneActionCompleted")
				).thenDo(
					new Step(
						new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR),
						new AsyncAction(new TestTaskAsync(TestTaskAsync.FAIL),Action.PROCEED_ON_ERROR)
					)
				)
			);
			
			
			s.addEventListener(StepEvent.STEP_COMPLETE,OnStepFailOrComplete);
			s.addEventListener(StepErrorEvent.STEP_ERROR,OnStepFailOrComplete);
			Async.proceedOnEvent(this,s,StepEvent.STEP_COMPLETE,1000);
			//Async.failOnEvent(this,s,StepErrorEvent.STEP_ERROR,1000);
			s.run();
		}
		
		public function OnStepFailOrComplete(event:Event):void
		{
			trace(event);
			Assert.assertEquals(2,event.target.getErrorStack().length);
		}
		
		
		[Test(async,timeout="1000")]
		
		public function testStepWithActions():void
		{
			var s:Step = new Step(
				new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR),
				new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR)
			).thenDo(
				new Step(
					new AsyncAction(new TestTaskAsync(TestTaskAsync.FAIL),Action.PROCEED_ON_ERROR),
					new AsyncAction(new TestTaskAsync(TestTaskAsync.FAIL),Action.PROCEED_ON_ERROR)
				).thenIf(
					new Condition(StepConditions,"AtLeastOneActionCompleted")
				).thenDo(
					new Step(
						new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR),
						new AsyncAction(new TestTaskAsync(TestTaskAsync.FAIL),Action.PROCEED_ON_ERROR)
					)
				)
			);
			
			
			s.addEventListener(StepEvent.STEP_COMPLETE,OnStepFailOrComplete2);
			s.addEventListener(StepErrorEvent.STEP_ERROR,OnStepFailOrComplete2);
			//Async.proceedOnEvent(this,s,StepEvent.STEP_COMPLETE,1000);
			Async.proceedOnEvent(this,s,StepErrorEvent.STEP_ERROR,1000);
			s.run();
		}
		
		public function OnStepFailOrComplete2(event:Event):void
		{
			trace(event);
			Assert.assertEquals(2,event.target.getErrorStack().length);
		}
		
		
		[Test(async,timeout="1000")]
		public function testStepWithActionsThatFailFatally():void
		{
			var s:Step = new Step(
				new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR),
				new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR)
			).thenDo(
				new Step(
					new AsyncAction(new TestTaskAsync(TestTaskAsync.FAIL),Action.PROCEED_ON_ERROR),
					new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR)
				).thenIf(
					new Condition(StepConditions,"AtLeastOneActionCompleted")
				).thenDo(
					new Step(
						new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR),
						new AsyncAction(new TestTaskAsync(TestTaskAsync.FAIL,20),Action.BREAK_ON_ERROR)
					)
				)
			);
			
			
			s.addEventListener(StepEvent.STEP_COMPLETE,OnStepFailOrComplete3);
			s.addEventListener(StepErrorEvent.STEP_ERROR,OnStepFailOrComplete3);
			//Async.proceedOnEvent(this,s,StepEvent.STEP_COMPLETE,1000);
			Async.proceedOnEvent(this,s,StepErrorEvent.STEP_ERROR,1000);
			s.run();
		}
		
		public function OnStepFailOrComplete3(event:Event):void
		{
			trace(event);
			Assert.assertEquals(2,event.target.getErrorStack().length);
			
			var err:StepErrorEvent = event as StepErrorEvent;
			trace(err.errorTarget.errorEvent);
			
			var firstFailedAction:Action = event.target.getErrorStack()[0];
			trace(firstFailedAction.errorEvent);
			Assert.assertTrue(firstFailedAction.errorEvent is ErrorEvent && !(firstFailedAction.errorEvent is StepErrorEvent));
		}
		
		[Test(async,timeout="1000")]
		public function testOtherwise():void
		{
			var s:Step = new Step(
				new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR),
				new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR)
			).thenDo(
				new Step(
					new AsyncAction(new TestTaskAsync(TestTaskAsync.FAIL),Action.PROCEED_ON_ERROR),
					new AsyncAction(new TestTaskAsync(TestTaskAsync.FAIL),Action.PROCEED_ON_ERROR)
				).thenIf(
					new Condition(StepConditions,"AtLeastOneActionCompleted")
				).thenDo(
					new Step(
						new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR),
						new AsyncAction(new TestTaskAsync(TestTaskAsync.FAIL),Action.PROCEED_ON_ERROR)
					)
				).otherwise(
					new Step(
						new AsyncAction(new TestTaskAsync(TestTaskAsync.SUCCEED),Action.PROCEED_ON_ERROR),
						new AsyncAction(new TestTaskAsync(TestTaskAsync.FAIL),Action.PROCEED_ON_ERROR)
					)
				)
			);
			
			s.addEventListener(StepEvent.STEP_COMPLETE,OnStepFailOrComplete4);
			s.addEventListener(StepErrorEvent.STEP_ERROR,OnStepFailOrComplete4);
			
			//Async.proceedOnEvent(this,s,StepErrorEvent.STEP_ERROR,1000);
			Async.proceedOnEvent(this,s,StepEvent.STEP_COMPLETE,1000);
			
			s.run();
		}
		
		public function OnStepFailOrComplete4(event:Event):void
		{
			trace(event);
			// because we have an "otherwise" - the condition failure does not cause an abort.
			// So by the end of the stack we should have 3 non critical errors
			Assert.assertEquals(3,Step(event.target).getErrorStack().length);
			
			
			var firstFailedAction:Action = event.target.getErrorStack()[0];
			trace(firstFailedAction.errorEvent);
			Assert.assertTrue(firstFailedAction.errorEvent is ErrorEvent && !(firstFailedAction.errorEvent is StepErrorEvent));
		}
	}
}